package scanner

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"buf.build/gen/go/safedep/api/grpc/go/safedep/services/malysis/v1/malysisv1grpc"
	malysisv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1"
	packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
	malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/malysis/v1"
	"github.com/safedep/dry/adapters"
	"google.golang.org/grpc"

	"github.com/safedep/vet/pkg/common/logger"
	"github.com/safedep/vet/pkg/common/utils"
	"github.com/safedep/vet/pkg/models"
)

type malysisMalwareAnalysisQueryEnricher struct {
	cc     *grpc.ClientConn
	client malysisv1grpc.MalwareAnalysisServiceClient
	config MalysisMalwareEnricherConfig
	gha    *adapters.GithubClient
}

var _ PackageMetaEnricher = &malysisMalwareAnalysisQueryEnricher{}

// NewMalysisMalwareAnalysisQueryEnricher creates a new malware analysis query enricher.
// We are re-using the config from the malware enricher because this enricher is a subset
// of the malware enricher.
func NewMalysisMalwareAnalysisQueryEnricher(cc *grpc.ClientConn,
	gha *adapters.GithubClient,
	config MalysisMalwareEnricherConfig,
) (*malysisMalwareAnalysisQueryEnricher, error) {
	if cc == nil {
		return nil, errors.New("grpc client connection is required")
	}

	if gha == nil {
		return nil, errors.New("github client is required")
	}

	client := malysisv1grpc.NewMalwareAnalysisServiceClient(cc)
	return &malysisMalwareAnalysisQueryEnricher{
		cc:     cc,
		client: client,
		config: config,
		gha:    gha,
	}, nil
}

func (e *malysisMalwareAnalysisQueryEnricher) Name() string {
	return "malysis-malware-analysis-query-enricher"
}

func (e *malysisMalwareAnalysisQueryEnricher) Enrich(pkg *models.Package, cb PackageDependencyCallbackFn) error {
	logger.Infof("[Malware Analysis] Enriching package with malware analysis query: %s/%s/%s",
		pkg.Manifest.Ecosystem, pkg.PackageDetails.Name, pkg.PackageDetails.Version)

	req := malysisv1.QueryPackageAnalysisRequest{
		Target: &malysisv1pb.PackageAnalysisTarget{
			PackageVersion: &packagev1.PackageVersion{
				Package: &packagev1.Package{
					Ecosystem: pkg.GetControlTowerSpecEcosystem(),
					Name:      pkg.GetName(),
				},
				Version: pkg.GetVersion(),
			},
		},
	}

	ctx, cancelFn := context.WithTimeout(context.Background(), e.config.GrpcOperationTimeout)
	defer cancelFn()

	if req.GetTarget().GetPackageVersion().GetPackage().GetEcosystem() == packagev1.Ecosystem_ECOSYSTEM_GITHUB_ACTIONS {
		logger.Infof("[Malware Analysis] Resolving commit hash for GitHub Actions package: %s/%s",
			pkg.GetName(), pkg.GetVersion())

		parts := strings.Split(pkg.GetName(), "/")
		if len(parts) != 2 {
			return fmt.Errorf("invalid package name: %s for GitHub Actions - should be in the format <owner>/<repo>", pkg.GetName())
		}

		commitHash, err := utils.ResolveGitHubRepositoryCommitSHA(ctx, e.gha, parts[0], parts[1], pkg.GetVersion())
		if err != nil {
			return err
		}

		logger.Infof("[Malware Analysis] Resolved commit hash for GitHub Actions package: %s/%s@%s",
			parts[0], parts[1], commitHash)

		req.GetTarget().GetPackageVersion().Version = commitHash
	}

	res, err := e.client.QueryPackageAnalysis(ctx, &req)
	if err != nil {
		return fmt.Errorf("failed to query package analysis: %w", err)
	}

	logger.Infof("[Malware Analysis] Applying package analysis results with analysisId: %s to package: %s/%s/%s",
		res.GetAnalysisId(), pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion())

	pkg.SetMalwareAnalysisResult(&models.MalwareAnalysisResult{
		AnalysisId:         res.GetAnalysisId(),
		Report:             res.GetReport(),
		VerificationRecord: res.GetVerificationRecord(),
	})

	return nil
}

func (e *malysisMalwareAnalysisQueryEnricher) Wait() error {
	return nil
}
